//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package tasks

import (
	"context"
	"encoding/json"

	"atomgit.com/openlinksaas/api-server/api_common"
	bg_runner_core "atomgit.com/openlinksaas/api-server/bg_runner/core"
	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/event_dao"
	"atomgit.com/openlinksaas/api-server/dao/project_dao"
	"atomgit.com/openlinksaas/api-server/dao/user_dao"
	"atomgit.com/openlinksaas/proto-gen-go.git/events_api"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/mongo"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/anypb"
)

type SetSourceUserMapping struct {
	ProjectId      string `json:"projectId"`
	EventSourceId  string `json:"eventSourceId"`
	SourceUserName string `json:"sourceUserName"`
	MapUserId      string `json:"mapUserId"`
}

func (t *SetSourceUserMapping) TaskType() string {
	return "SetSourceUserMapping"
}

func (t *SetSourceUserMapping) Execute(logger *zap.Logger) error {
	//检查项目是否存在
	exist, err := project_dao.ProjectInfoDao.Exist(context.Background(), t.ProjectId)
	if err != nil {
		logger.Error("check project exist failed", zap.String("projectId", t.ProjectId), zap.Error(err))
		return err
	}
	if !exist {
		logger.Debug("project not exist", zap.String("projectId", t.ProjectId))
		return nil
	}
	projectItem, err := project_dao.ProjectInfoDao.Get(context.Background(), t.ProjectId)
	if err != nil {
		logger.Error("get project info failed", zap.String("projectId", t.ProjectId), zap.Error(err))
		return err
	}
	if projectItem.Closed {
		logger.Debug("project is closed", zap.String("projectId", t.ProjectId), zap.Error(err))
		return nil
	}
	//检查mapuser是否存在
	exist, err = project_dao.MemberInfoDao.Exist(context.Background(), t.ProjectId, t.MapUserId)
	if err != nil {
		logger.Error("check map user exist failed", zap.String("projectId", t.ProjectId), zap.String("mapUserId", t.MapUserId), zap.Error(err))
		return err
	}
	if !exist {
		logger.Debug("map user not exist", zap.String("projectId", t.ProjectId), zap.String("mapUserId", t.MapUserId))
		return nil
	}
	userItem, err := user_dao.UserInfoDao.Get(context.Background(), t.MapUserId)
	if err != nil {
		logger.Error("get map user info failed", zap.String("projectId", t.ProjectId), zap.String("mapUserId", t.MapUserId), zap.Error(err))
		return err
	}
	// memberItem, err := project_dao.MemberInfoDao.Get(context.Background(), t.ProjectId, t.MapUserId, true, false)
	// if err != nil {
	// 	logger.Error("get map user info failed", zap.String("projectId", t.ProjectId), zap.String("mapUserId", t.MapUserId), zap.Error(err))
	// 	return err
	// }
	//列出暂存的事件
	stageEventItemList, err := event_dao.EventStageDao.ListBySourceUser(context.Background(), t.ProjectId, t.EventSourceId, t.SourceUserName)
	if err != nil {
		logger.Error("list stage event failed", zap.String("projectId", t.ProjectId), zap.String("eventSourceId", t.EventSourceId),
			zap.String("sourceUserName", t.SourceUserName), zap.String("mapUserId", t.MapUserId), zap.Error(err))
		return err
	}
	logger.Info("list stage event", zap.String("projectId", t.ProjectId), zap.String("eventSourceId", t.EventSourceId),
		zap.String("sourceUserName", t.SourceUserName), zap.String("mapUserId", t.MapUserId), zap.Int("count", len(stageEventItemList)))
	for _, stageEventItem := range stageEventItemList {
		err = t.realProcess(stageEventItem, projectItem, userItem, logger)
		if err != nil {
			return err
		}
	}
	return nil
}

func (t *SetSourceUserMapping) realProcess(stageEventItem *event_dao.EventStageDbItem, projectItem *project_dao.ProjectInfoDbItem, userItem *user_dao.UserInfoDbItem, logger *zap.Logger) error {
	eventData := &anypb.Any{}
	err := proto.Unmarshal(stageEventItem.EventData, eventData)
	if err != nil {
		logger.Error("unmarshal event data failed", zap.String("projectId", stageEventItem.ProjectId), zap.String("eventSourceId", stageEventItem.EventSourceId),
			zap.String("eventStageId", stageEventItem.EventStageId), zap.Error(err))
		return err
	}
	event := &events_api.Event{
		EventId:         uuid.NewString(),
		UserId:          t.MapUserId,
		UserDisplayName: userItem.BasicInfo.DisplayName,
		ProjectId:       t.ProjectId,
		ProjectName:     projectItem.BasicInfo.ProjectName,
		EventType:       events_api.EVENT_TYPE(stageEventItem.EventType),
		EventTime:       stageEventItem.EventTime,
		RefType:         events_api.EVENT_REF_TYPE_EVENT_REF_TYPE_NONE,
		RefId:           "",
		EventData:       eventData,
	}
	dbSess, err := dao.StartSession()
	if err != nil {
		return err
	}
	defer dao.EndSession(dbSess)
	_, err = dbSess.WithTransaction(context.Background(), func(sessCtx mongo.SessionContext) (interface{}, error) {
		err := api_common.AddEvent(sessCtx, event, false, stageEventItem.EventSourceId, stageEventItem.SourceUserName)
		if err != nil {
			logger.Error("add event failed", zap.String("projectId", stageEventItem.ProjectId), zap.String("eventSourceId", stageEventItem.EventSourceId), zap.Error(err))
			return nil, err
		}
		err = event_dao.EventStageDao.Remove(sessCtx, stageEventItem.EventStageId, t.ProjectId, t.EventSourceId, stageEventItem.SourceUserName)
		if err != nil {
			logger.Error("remove stage event failed", zap.String("projectId", stageEventItem.ProjectId), zap.String("eventSourceId", stageEventItem.EventSourceId),
				zap.String("eventStageId", stageEventItem.EventStageId), zap.Error(err))
			return nil, err
		}
		return nil, nil
	})

	return err
}

func (t *SetSourceUserMapping) NewInstance(jsonData []byte) (bg_runner_core.BackGroundTask, error) {
	obj := &SetSourceUserMapping{}
	err := json.Unmarshal(jsonData, obj)
	if err != nil {
		return nil, err
	}
	return obj, nil
}
